﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace FCMGAAI
{
    public class FCM1
    {
        public int panjangBarisData;
        public int PanjangKolomData;
        public int panjangKolomParameterData;
        public int JumlahCluster;
        public int MaxIterasi; // jumlah iterasi maksimal
        public int FinalIterasi; // dapatkan indeks iterasi terakhir
        public double Precission; //akurasi 
        public double RecentError; //akurasi pada looping terakhir
        public double Fuzziness; // derajat fuzzynes default =2
        public double JM; // Nilai Fungsi Objektif

        public string[] NamaLabel; //label dari data
        public double[,] MDataAsli;
        public string[] ClusterAsli; //cluster asli dari data
        public double[] ClusterFCM; //cluster hasil FCM
        //public int[] ClusterFCMGenap; //Cluster hasil FCM Penggenapan Desimal
        public double[,] MderajatKeanggotaan; // matriks derajat keanggotaan
        public double[,] MpusatCluster; // matriks pusat cluster
        public int[] letak;
        public bool isRunned = false;

        public List<EcoliModel> Ecoli;

        public void setParameter(int jmlClstr, double tgktKabur, int maxIter, double akur)
        {
            Fuzziness = tgktKabur;
            JumlahCluster = jmlClstr;
            MaxIterasi = maxIter;
            Precission = akur;
            MderajatKeanggotaan = new double[panjangBarisData, JumlahCluster];// tiap baris data memiliki keanggotaan sebanyak cluster
            MpusatCluster = new double[JumlahCluster, panjangKolomParameterData]; //tiap parameter memiliki nilai pusat cluster 
            ClusterFCM = new double[panjangBarisData];
            letak = new int[panjangBarisData];
            

        }
        /// <summary>
        /// Metod untuk proses data dari txt ke dalam array data asli
        /// </summary>
        /// <param name="TextFile"> URL File Input</param>
        /// <param name="ErrMSg"> Error Message Output</param>
        /// <returns> true jika berhasil </returns>
        /// 

        public int GetPanjangBarisColoumnData(String PathFile,ref int PanjangKolom)
        {
            int PanjangBaris = 0;
            String BarisTemp = "";
            FileStream FS = new FileStream(PathFile, FileMode.Open, FileAccess.Read);
            StreamReader SR = new StreamReader(FS);
            BarisTemp = SR.ReadLine();
            PanjangBaris++;
            PanjangKolom = BarisTemp.Split('|').Length;
            while ((SR.ReadLine()) != null)
            {
                PanjangBaris++;
            }
            return PanjangBaris;
        }
        public bool ProcessInputFile(String PathFile, ref String ErrMSg)
        {
            ShareVariable.IsFCMRunned = false;
            ShareVariable.isGARunned = false;
            Ecoli = new List<EcoliModel>();
            int idxColumn = 0;
            int idxBaris = 0;
            try
            {
                panjangBarisData = GetPanjangBarisColoumnData(PathFile,ref PanjangKolomData);
                panjangKolomParameterData = PanjangKolomData - 2;

                NamaLabel = new String[panjangBarisData];
                ClusterAsli = new String[panjangBarisData];
                MDataAsli = new double[panjangBarisData,panjangKolomParameterData];
                FileStream FS = new FileStream(PathFile, FileMode.Open, FileAccess.Read);
                StreamReader SR = new StreamReader(FS);
                String TextPerLine = "";

                while ((TextPerLine = SR.ReadLine()) != null)
                {
                    EcoliModel _ecoli = new EcoliModel();
                    string[] perColoumn = TextPerLine.Split('|');
                    NamaLabel[idxBaris] = perColoumn[0];
                    _ecoli.nama_ecoli = NamaLabel[idxBaris];
                    _ecoli.K1 = Convert.ToDouble(perColoumn[1]);
                    _ecoli.K2 = Convert.ToDouble(perColoumn[2]);
                    _ecoli.K3 = Convert.ToDouble(perColoumn[3]);
                    _ecoli.K4 = Convert.ToDouble(perColoumn[4]);
                    _ecoli.K5 = Convert.ToDouble(perColoumn[5]);
                    _ecoli.K6 = Convert.ToDouble(perColoumn[6]);
                    _ecoli.K7 = Convert.ToDouble(perColoumn[7]);
                    _ecoli.ClusterAsli = perColoumn[8];
                    _ecoli.HasilClustering = 0;
                    Ecoli.Add(_ecoli);
                    ClusterAsli[idxBaris] = perColoumn[perColoumn.Length - 1];
                    for (int i = 1; i < perColoumn.Length -1; i++)
                    {
                        MDataAsli[idxBaris, idxColumn] = Convert.ToDouble(perColoumn[i]);
                        idxColumn++;
                    }
                    idxColumn = 0;
                    idxBaris++;
                }
                ErrMSg = "process input file berhasil";
                return true;
            }
            catch (Exception ex)
            {
                ErrMSg = ex.InnerException.Message;
                return false;
            }

        }

        //Random matriks derajat keanggotaan awal
        public void SetMDerajatKeanggotaanAwal()
        {
            Random random1 = new Random();
            Random TrueRandom = new Random((int)DateTime.Now.Ticks & (0x0000FFFF + random1.Next())); ;

            for (int i = 0; i < panjangBarisData; i++)
            {
                double jumlah = 0;
                for (int k = 0; k < JumlahCluster; k++)
                {
                    MderajatKeanggotaan[i, k] =  TrueRandom.Next();
                    jumlah += MderajatKeanggotaan[i, k];
                }
                for (int k = 0; k < JumlahCluster; k++)
                {
                    MderajatKeanggotaan[i, k] /= jumlah;
                    MderajatKeanggotaan[i, k] = Math.Round(MderajatKeanggotaan[i, k], 3);
                }
            }
        }

        //Rumus ke 5 di paper
        public void SetMPusatCluster()
        {
            double[,] atas = new double[JumlahCluster, panjangKolomParameterData + 1];
            double[,] bawah = new double[panjangBarisData + 1, JumlahCluster];
            double[] totalBawah = new double[JumlahCluster];

            for (int k = 0; k < JumlahCluster; k++)
            {
                for (int j = 0; j < panjangKolomParameterData; j++)
                {
                    for (int i = 0; i < panjangBarisData; i++)
                    {
                        atas[k, j] += Math.Pow(MderajatKeanggotaan[i, k], Fuzziness) * MDataAsli[i, j];
                    }
                }
            }

            for (int k = 0; k < JumlahCluster; k++)
            {
                for (int i = 0; i < panjangBarisData; i++)
                {
                    bawah[i, k] = Math.Pow(MderajatKeanggotaan[i, k], Fuzziness);
                    totalBawah[k] += bawah[i, k];
                }
            }

            for (int k = 0; k < JumlahCluster; k++)
            {
                for (int j = 0; j < panjangKolomParameterData; j++)
                {
                    MpusatCluster[k, j] = atas[k, j] / totalBawah[k];
                    MpusatCluster[k, j] = Math.Round(MpusatCluster[k, j], 3);
                }
            }
        }

        //Rumus ke 1 di paper
        public double HitungfungsiObjektif()
        {
            double[,] Jm1 = new double[panjangBarisData + 1, JumlahCluster];
            double[,] Jm2 = new double[panjangBarisData + 1, JumlahCluster];
            double FObj = 0;

            for (int k = 0; k < JumlahCluster; k++)
            {
                for (int i = 0; i < panjangBarisData; i++)
                {
                    for (int j = 0; j <= panjangKolomParameterData - 1; j++)
                    {
                        Jm1[i, k] += (Math.Pow(MDataAsli[i, j] - MpusatCluster[k, j], 2)); //note
                    }
                }
            }

            for (int i = 0; i < panjangBarisData; i++)
            {
                for (int k = 0; k < JumlahCluster; k++)
                {
                    Jm2[i, k] = Jm1[i, k] * (Math.Pow(MderajatKeanggotaan[i, k], Fuzziness));
                    FObj += Jm2[i, k];
                    FObj = Math.Round(FObj, 5);
                }
            }
            return FObj;
        }

        //rumus ke 6 di paper
        public void updateMatrikDerajatKeanggotaan()
        {
            double[,] atas = new double[panjangBarisData + 1, JumlahCluster];
            double[] bawah = new double[panjangBarisData + 1];

            for (int k = 0; k < JumlahCluster; k++)
            {
                for (int i = 0; i < panjangBarisData; i++)
                {
                    for (int j = 0; j <= panjangKolomParameterData - 1; j++)
                    {
                        atas[i, k] += (Math.Pow((MDataAsli[i, j] - MpusatCluster[k, j]), 2));
                    }
                }
            }
            for (int i = 0; i < panjangBarisData; i++)
            {
                for (int k = 0; k < JumlahCluster; k++)
                {
                   // double pangkat = (-1) / (w - 1);
                    atas[i, k] = Math.Pow((atas[i, k]), 2);
                    bawah[i] += atas[i, k];
                }
            }
            for (int i = 0; i < panjangBarisData; i++)
            {
                for (int k = 0; k < JumlahCluster; k++)
                {
                    MderajatKeanggotaan[i, k] = atas[i, k] / bawah[i];
                    MderajatKeanggotaan[i, k] = Math.Round(MderajatKeanggotaan[i, k], 5);
                }
            }
        }


        //tentukan Cluster hasil FCM
        public void SetAnggotaCluster()
        {
            //simpan posisi index
            letak = new int[panjangBarisData];

            for (int i = 0; i < MderajatKeanggotaan.GetLength(0); i++) //jumlah baris
            {
                letak[i] = 1;
                ClusterFCM[i] = MderajatKeanggotaan[i, 0];

                for (int j = 0; j < MderajatKeanggotaan.GetLength(1); j++) //jumlah cluster
                {
                    if (MderajatKeanggotaan[i, j] > ClusterFCM[i])
                    {
                        ClusterFCM[i] = MderajatKeanggotaan[i, j];
                        letak[i] = j + 1;
                        Ecoli[i].HasilClustering = letak[i];
                    }
                }
            }
        }

        //cek kondisi berhenti iterasi FCM

        public void FCMOperate()
        {
            double TempJM = 0;
            for (int iterasi = 1; iterasi <= MaxIterasi; iterasi++)
            {
                TempJM = JM;
                SetMPusatCluster();
                JM = HitungfungsiObjektif();
                updateMatrikDerajatKeanggotaan();
                SetAnggotaCluster();
                FinalIterasi = iterasi;
                RecentError = Math.Abs(JM - TempJM);
                if (RecentError < Precission)
                {
                    break;
                }
                
            }
            Finishing();
        }

        //public int[] GetCountWorngCluster()
        //{
        //    int[] TempCountWorngCluster = new int[JumlahCluster];
            
        //    return TempCountWorngCluster;
        //}

        public void Finishing()
        {
            isRunned = true;
            ShareVariable.IsFCMRunned = true;
            ShareVariable.MatriksUdariFCM = new double[MderajatKeanggotaan.GetLength(0), MderajatKeanggotaan.GetLength(1)];
            ShareVariable.MatriksPusatClusterFCM = new double[MpusatCluster.GetLength(0), MpusatCluster.GetLength(1)];
            ShareVariable.letakFCM = new int[letak.Length];

            for (int i = 0; i < MderajatKeanggotaan.GetLength(0); i++)
            {
                for (int j = 0; j < MderajatKeanggotaan.GetLength(1); j++)
                {
                    ShareVariable.MatriksUdariFCM[i, j] = MderajatKeanggotaan[i, j];
                }
            }

            for (int i = 0; i < MpusatCluster.GetLength(0); i++)
            {
                for (int j = 0; j < MpusatCluster.GetLength(1); j++)
                {
                    ShareVariable.MatriksPusatClusterFCM[i, j] = MpusatCluster[i, j];
                }
            }

            for (int i = 0; i < letak.Length; i++)
            {
                ShareVariable.letakFCM[i] = letak[i];
            }
        }
    }
}
